﻿using AgCIM.Tools.SpatialEncoding.Beidou.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AgCIM.Tools.SpatialEncoding.Beidou
{
    public class BeidouGridCellCompute
    {
        private const double EarthRadius = 6378137.0;
        private readonly BeidouBoundingBox boundingBox;
        private readonly List<(double, double)> meshArray;

        public BeidouGridCellCompute(BeidouBoundingBox bbox)
        {
            var boundingbox = new BeidouBoundingBox(
            new Coordinate { Lng = bbox.MinLon, Lat = bbox.MinLat, Alt = bbox.MinAlt },
            new Coordinate { Lng = bbox.MaxLon, Lat = bbox.MaxLat, Alt = bbox.MaxAlt },
            true
            );
            this.boundingBox = boundingbox.ToRadians();
            this.meshArray = new List<(double, double)>
            {
                (90, 44),
                (6, 4),
                (0.5, 0.5),
                (0.25, 10 / 60.0),
                (1 / 60.0, 1 / 60.0),
                (4 / 3600.0, 4 / 3600.0),
                (2 / 3600.0, 2 / 3600.0),
                (1 / (4 * 3600.0), 1 / (4 * 3600.0)),
                (1 / (32 * 3600.0), 1 / (32 * 3600.0)),
                (1 / (256 * 3600.0), 1 / (256 * 3600.0)),
                (1 / (2048 * 3600.0), 1 / (2048 * 3600.0))
            };
        }

        /// <summary>
        /// 判断当前视野的网格划分级别
        /// </summary>
        /// <param name="isFromMax">是否从最大值开始</param>
        /// <returns>北斗层级</returns>
        public BeidouLevel JudgeLodMesh(bool isFromMax = false)
        {
            BeidouLevel beidouLevel = null;

            double detaX = this.boundingBox.GetLongitudeSpan();
            double detaY = this.boundingBox.GetLatitudeSpan();
            int xLevel = 10;
            int yLevel = 10;

            for (int i = 0; i < this.meshArray.Count; i++)
            {
                if (detaX >= LatLonHelper.Degree2rad(this.meshArray[i].Item1))
                {
                    xLevel = i;
                    break;
                }
            }

            for (int i = 0; i < this.meshArray.Count; i++)
            {
                if (detaY >= LatLonHelper.Degree2rad(this.meshArray[i].Item2))
                {
                    yLevel = i;
                    break;
                }
            }

            int index = 0;
            if (isFromMax)
            {
                index = xLevel < yLevel ? xLevel : yLevel;
            }
            else
            {
                index = xLevel < yLevel ? yLevel : xLevel;
            }

            if (this.boundingBox.GetLatitudeSpan() == Math.PI)
            {
                beidouLevel = new BeidouLevel(0, (90, 44));
            }
            else
            {
                beidouLevel = new BeidouLevel(index, this.meshArray[index]);
            }

            return beidouLevel;
        }

        /// <summary>
        /// 判断实体高度所在层级
        /// </summary>
        /// <returns>北斗层级</returns>
        public BeidouLevel JudgeHeightLodMesh()
        {
            BeidouLevel beidouLevel = null;
            double detaHeight = this.boundingBox.GetHeightSpan();

            for (int i = 1; i < this.meshArray.Count; i++)
            {
                double hForward = GetExtrudeHeight(1, this.meshArray[i].Item1);
                double hBack = GetExtrudeHeight(1, this.meshArray[i - 1].Item1);

                if (detaHeight > hForward && detaHeight < hBack)
                {
                    beidouLevel = new BeidouLevel(i, this.meshArray[i]);
                    return beidouLevel;
                }
            }

            beidouLevel = new BeidouLevel(10, this.meshArray[10]);
            return beidouLevel;
        }

        /// <summary>
        /// 获取对应的高度值
        /// </summary>
        /// <param name="n">高度编码</param>
        /// <param name="ceta">划分单元网格对应的值</param>
        /// <returns>高度值</returns>
        public double GetExtrudeHeight(int n, double ceta)
        {
            double c0 = Math.PI / 180.0;
            double lng = ceta;
            return Math.Pow(1 + c0, (n - 1) * lng) * EarthRadius * (Math.Pow(1 + c0, lng) - 1);
        }
    }
}
